<?php
namespace Kangcg\Tencent\Library\V3;

use Kangcg\Application\Helper\Curl;

/**
 * V3签名
 */
trait V3{
    public function httpRequest(array $data, $url, string $action, $version = '2019-12-05', $region = '', array $headers = [], $method = Curl::METHOD_POST)
    {
        $action = ucfirst($action);
        $headers = $this->getHeaders($data, $url, $action, $version, $region, $headers, $method);
        $curl = Curl::getInstall();
        $curl->setHeader($headers);
        if (!$result = $curl->request($url, json_encode($data, true), $method)) {
            return $this->setErrors('-1', $curl->getError());
        }

        $result = json_decode($result, true);
        if(isset($result['Response']['Error'])){
            return $this->setErrors( $result['Response']['Error']['Code'], $result['Response']['Error']['Message']);
        }

        return $result;
    }

    private function getHeaders(array $data, $url, string $action, $version = null, $region = '', array $headers = [], $method = Curl::METHOD_POST)
    {
        $headers["Content-Type"] = 'application/json; charset=utf-8';
        $headers["Host"] = parse_url($url, PHP_URL_HOST);
        $headers["X-TC-Action"] = ucfirst($action);
        $headers["X-TC-Timestamp"] = time();
        $headers["X-TC-Version"] = $version;
        $headers["X-TC-Language"] = 'zh-CN';
        if($this->region) $headers["X-TC-Region"] = $this->region;
        $headers["Authorization"] = $this->getAuthorization($headers["Host"], $headers["X-TC-Action"], $data, $method, $headers['X-TC-Timestamp']);

        return $headers;
    }

    private function getSignature($date, $service, $stringToSign)
    {
        $secretDate = $this->hashHmac("TC3" . $this->secretKey, $date);
        $secretService = $this->hashHmac($secretDate, $service);
        $secretSigning = $this->hashHmac($secretService, "tc3_request");
        $signature = hash_hmac("sha256", $stringToSign, $secretSigning);

        return $signature;
    }
    private function getAuthorization($host, $action, $data, $method, $timestamp)
    {
        $service = explode(".", $host)[0];
        $date = gmdate("Y-m-d", $timestamp);
        $signedHeaders = $this->getSignedHeaders();
        $canonicalRequest = $this->getCanonicalRequest($host, $action, $data, $method, $signedHeaders);
        $stringToSign = $this->getStringToSign($canonicalRequest, $service, $date, $timestamp);
        $signature = $this->getSignature($date, $service, $stringToSign);
        $credentialScope = $date . "/" . $service . "/tc3_request";

        $authorization = static::$ALGORITHM
            ." Credential=". $this->secretId . "/" . $credentialScope
            .", SignedHeaders=".$signedHeaders.", Signature=".$signature;

        return $authorization;
    }

    private function hashHmac(string $key, string $msg, string $algo = 'sha256')
    {
        return hash_hmac($algo, $msg, $key, true);
    }

    private function getStringToSign($canonicalRequest, $service, $date, $timestamp)
    {
        $credentialScope = $date . "/" . $service . "/tc3_request";
        $hashedCanonicalRequest = hash("sha256", $canonicalRequest);

        return static::$ALGORITHM . "\n"
            . $timestamp . "\n"
            . $credentialScope . "\n"
            . $hashedCanonicalRequest;
    }
    private function getCanonicalRequest($host, $action, $data, $method, $signedHeaders)
    {
        $canonicalUri = "/";
        $canonicalQueryString = "";
        $canonicalHeaders = $this->getCanonicalHeaders($host, $action);
        $hashedRequestPayload = $this->getHashedRequestPayload($data, $method);
        $canonicalRequest = $method . "\n"
            . $canonicalUri . "\n"
            . $canonicalQueryString . "\n"
            . $canonicalHeaders . "\n"
            . $signedHeaders . "\n"
            . $hashedRequestPayload;

        return $canonicalRequest;
    }

    private function getCanonicalHeaders($host, $action)
    {
        return implode("\n", [
            "content-type:application/json; charset=utf-8",
            "host:" . $host,
            "x-tc-action:" . strtolower($action),
            ""
        ]);
    }

    private function getSignedHeaders()
    {
        return implode(";", [
            "content-type",
            "host",
            "x-tc-action",
        ]);
    }

    private function getHashedRequestPayload($data, $method)
    {
        if ($method != Curl::METHOD_POST) {
            $payload = 'UNSIGNED-PAYLOAD';
        } else {
            $payload = json_encode($data);
        }

        return hash("SHA256", $payload);
    }
    public static $ALGORITHM = 'TC3-HMAC-SHA256';
}
